package gqqnbig.swing;

import java.awt.BorderLayout;
import java.awt.Dimension;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.KeyAdapter;
import java.awt.event.KeyEvent;
import java.util.ArrayList;

import javax.swing.DefaultComboBoxModel;
import javax.swing.JComboBox;
import javax.swing.JPanel;
import javax.swing.JTextField;
import javax.swing.event.DocumentEvent;
import javax.swing.event.DocumentListener;

public class AutoCompleteTextField extends JPanel
{
	private final JTextField textField;

	private boolean isChangingText;

	public AutoCompleteTextField(ArrayList<String> items)
	{
		super(new BorderLayout());

		textField = new JTextField();
		setupAutoComplete(textField, items);
		// textField.setPreferredSize(new Dimension(200, 20));
		// txtInput.setColumns(30);
		add(textField, BorderLayout.NORTH);
	}

	@Override
	public void setPreferredSize(Dimension preferredSize)
	{
		// TODO Auto-generated method stub
		super.setPreferredSize(preferredSize);
		textField.setPreferredSize(preferredSize);
	}

	public String getText()
	{
		return textField.getText();
	}

	public void setText(String text)
	{
		isChangingText = true;
		textField.setText(text);
		isChangingText = false;
	}

	private static boolean isAdjusting(JComboBox<String> cbInput)
	{
		if (cbInput.getClientProperty("is_adjusting") instanceof Boolean)
		{
			return (Boolean) cbInput.getClientProperty("is_adjusting");
		}
		return false;
	}

	private static void setAdjusting(JComboBox<String> cbInput, boolean adjusting)
	{
		cbInput.putClientProperty("is_adjusting", adjusting);
	}

	public void setupAutoComplete(final JTextField txtInput, final ArrayList<String> items)
	{
		final DefaultComboBoxModel model = new DefaultComboBoxModel();
		final JComboBox<String> comboBox = new JComboBox<String>(model)
		{
			@Override
			public Dimension getPreferredSize()
			{
				return new Dimension(super.getPreferredSize().width, 0);
			}
		};

		comboBox.setFocusable(false);

		setAdjusting(comboBox, false);
		for (String item : items)
		{
			model.addElement(item);
		}
		comboBox.setSelectedItem(null);
		comboBox.addActionListener(new ActionListener()
		{
			@Override
			public void actionPerformed(ActionEvent e)
			{
				if (!isAdjusting(comboBox))
				{
					if (comboBox.getSelectedItem() != null)
					{
						txtInput.setText(comboBox.getSelectedItem().toString());
					}
				}
			}
		});

		txtInput.addKeyListener(new KeyAdapter()
		{

			@Override
			public void keyPressed(KeyEvent e)
			{
				setAdjusting(comboBox, true);
				if (e.getKeyCode() == KeyEvent.VK_SPACE)
				{
					if (comboBox.isPopupVisible())
					{
						e.setKeyCode(KeyEvent.VK_ENTER);
					}
				}
				if (e.getKeyCode() == KeyEvent.VK_ENTER || e.getKeyCode() == KeyEvent.VK_UP || e.getKeyCode() == KeyEvent.VK_DOWN)
				{
					e.setSource(comboBox);
					comboBox.dispatchEvent(e);
					if (e.getKeyCode() == KeyEvent.VK_ENTER)
					{
						txtInput.setText(comboBox.getSelectedItem().toString());
						comboBox.setPopupVisible(false);
					}
				}
				if (e.getKeyCode() == KeyEvent.VK_ESCAPE)
				{
					comboBox.setPopupVisible(false);
				}
				setAdjusting(comboBox, false);
			}
		});
		txtInput.getDocument().addDocumentListener(new DocumentListener()
		{
			public void insertUpdate(DocumentEvent e)
			{
				if (isChangingText == false)
					updateList();
			}

			public void removeUpdate(DocumentEvent e)
			{
				if (isChangingText == false)
					updateList();
			}

			public void changedUpdate(DocumentEvent e)
			{
				if (isChangingText == false)
					updateList();
			}

			private void updateList()
			{
				setAdjusting(comboBox, true);
				model.removeAllElements();
				String input = txtInput.getText();
				if (input.isEmpty() == false)
				{
					for (String item : items)
					{
						if (item.toLowerCase().contains(input.toLowerCase()))
							model.addElement(item);
					}
				}
				else
				{
					for (String item : items)
						model.addElement(item);
				}
				comboBox.setPopupVisible(model.getSize() > 0);
				setAdjusting(comboBox, false);
			}
		});
		txtInput.setLayout(new BorderLayout());
		txtInput.add(comboBox, BorderLayout.SOUTH);

	}
}
